﻿// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

using System.Collections.Generic;
using System.Drawing;

namespace System.Windows.Forms
{
    public partial class ListViewItem : IKeyboardToolTip
    {
        private bool AllowsToolTips => listView?.ShowItemToolTips ?? false;

        bool IKeyboardToolTip.AllowsChildrenToShowToolTips() => AllowsToolTips;

        bool IKeyboardToolTip.AllowsToolTip() => true;

        bool IKeyboardToolTip.CanShowToolTipsNow() => AllowsToolTips;

        string IKeyboardToolTip.GetCaptionForTool(ToolTip toolTip) => ToolTipText;

        Rectangle IKeyboardToolTip.GetNativeScreenRectangle() => GetNativeRectangle(Index);

        IList<Rectangle> IKeyboardToolTip.GetNeighboringToolsRectangles()
        {
            List<Rectangle> neighboringRectangles = new List<Rectangle>();
            if (listView is null)
            {
                return neighboringRectangles;
            }

            switch (listView.View)
            {
                case View.SmallIcon:
                case View.LargeIcon:
                    foreach (SearchDirectionHint searchDirectionHint in Enum.GetValues(typeof(SearchDirectionHint)))
                    {
                        ListViewItem? neighboringItem = FindNearestItem(searchDirectionHint);

                        if (neighboringItem is not null)
                        {
                            neighboringRectangles.Add(GetNativeRectangle(neighboringItem.Index));
                        }
                    }

                    break;
                case View.Details:
                    neighboringRectangles.Add(AccessibilityObject.Bounds);

                    if (Index > 0)
                    {
                        neighboringRectangles.Add(listView.Items[Index - 1].AccessibilityObject.Bounds);
                    }

                    if (Index < listView.Items.Count - 1)
                    {
                        neighboringRectangles.Add(listView.Items[Index + 1].AccessibilityObject.Bounds);
                    }

                    break;
            }

            return neighboringRectangles;
        }

        IWin32Window IKeyboardToolTip.GetOwnerWindow() => listView;

        bool IKeyboardToolTip.HasRtlModeEnabled() => listView?.RightToLeft == RightToLeft.Yes;

        bool IKeyboardToolTip.IsBeingTabbedTo() => Control.AreCommonNavigationalKeysDown();

        bool IKeyboardToolTip.IsHoveredWithMouse() => listView?.AccessibilityObject.Bounds.Contains(Control.MousePosition) ?? false;

        void IKeyboardToolTip.OnHooked(ToolTip toolTip) => OnKeyboardToolTipHook(toolTip);

        void IKeyboardToolTip.OnUnhooked(ToolTip toolTip) => OnKeyboardToolTipUnhook(toolTip);

        bool IKeyboardToolTip.ShowsOwnToolTip() => AllowsToolTips;

        internal virtual void OnKeyboardToolTipHook(ToolTip toolTip) { }

        internal virtual void OnKeyboardToolTipUnhook(ToolTip toolTip) { }

        private Rectangle GetNativeRectangle(int index)
        {
            if (listView is null)
            {
                return Rectangle.Empty;
            }

            ListViewItem item = listView.Items[index];
            if (item.Group is not null && item.Group.CollapsedState == ListViewGroupCollapsedState.Collapsed)
            {
                return Rectangle.Empty;
            }

            Rectangle itemBounds = listView.GetItemRect(index, ItemBoundsPortion.Label);
            Rectangle listviewBounds = listView.AccessibilityObject.Bounds;
            Point point = new Point(listviewBounds.X + itemBounds.X, listviewBounds.Y + itemBounds.Y);

            switch (listView.View)
            {
                case View.Details:
                case View.List:
                    return GetDetailsListRectangle(point, item, itemBounds);
                case View.Tile:
                    return GetTileRectangle(point, item, itemBounds);
                default:
                    return new Rectangle(point, new Size(itemBounds.Width, itemBounds.Height));
            }
        }

        private Rectangle GetDetailsListRectangle(Point point, ListViewItem item, Rectangle itemBounds)
        {
            // The GetItemRect(index, ItemBoundsPortion.Label) method returns the width of the cell,
            // not the text for "Details" and "List" views. As result, if the text is smaller than the cell,
            // we return the width of the text. If the text is larger than the cell, we return the width of the cell
            return new Rectangle(
                point,
                new Size(Math.Min(TextRenderer.MeasureText(item.Text, item.Font).Width, itemBounds.Width),
                itemBounds.Height));
        }

        private Rectangle GetTileRectangle(Point point, ListViewItem item, Rectangle itemBounds)
        {
            // The GetItemRect(index, ItemBoundsPortion.Label) method returns the incorrect width of the item for "Tile" view.
            // When returning the rectangle, we take into account the width of the texts of the item and its sub-item,
            // because the first sub-item is also displayed in the list
            int textWidth = TextRenderer.MeasureText(item.Text, item.Font).Width;

            if (SubItems.Count > 1)
            {
                ListViewSubItem subItem = SubItems[1];
                textWidth = Math.Max(TextRenderer.MeasureText(subItem.Text, subItem.Font).Width, textWidth);
            }

            return new Rectangle(point, new Size(Math.Min(textWidth, itemBounds.Width), itemBounds.Height));
        }
    }
}
